#include "threadPool.h"

/* murmur_hash2 */
static uint32_t hash(const void *key, int len, uint32_t seed) {
  const uint32_t m = 0x5bd1e995;
  const int r = 24;
  uint32_t h = seed ^ len;
  const unsigned char *data = (const unsigned char *)key;

  while (len >= 4) {
    uint32_t k = *(uint32_t *)data;
    k *= m;
    k ^= k >> r;
    k *= m;
    h *= m;
    h ^= k;
    data += 4;
    len -= 4;
  }

  switch (len) {
  case 3:
    h ^= data[2] << 16;
  case 2:
    h ^= data[1] << 8;
  case 1:
    h ^= data[0];
    h *= m;
  };

  h ^= h >> 13;
  h *= m;
  h ^= h >> 15;

  return h;
}

userTable *user_table_create() {
  userTable *h = (userTable *)calloc(1, sizeof(userTable));
  if (!h) {
    fprintf(stderr, "calloc userTable failed in userTable.c:user_table_init\n");
    return NULL;
  }
  hashNode **data = (hashNode **)calloc(CAPACITY_INIT, sizeof(hashNode *));
  if (!data) {
    fprintf(stderr, "calloc buckets failed in userTable.c:user_table_init\n");
    free(h);
    return NULL;
  }
  h->buckets = data;
  h->size = 0;
  h->capacity = CAPACITY_INIT;
  h->seed = time(NULL);
  return h;
}

int user_table_init(userTable *h) {
  MYSQL *conn = mysql_init(NULL);
  MYSQL *ret = mysql_real_connect(conn, "127.0.0.1", "root", "123456",
                                  "netdisk", 3306, NULL, 0);
  if (!ret) {
    fprintf(stderr, "mysql_real_connect:%s\n", mysql_error(conn));
    return -1;
  }
  char sql[4096] = {0};
  sprintf(sql, "select user_id,user_name,salt,encrypt,prefix from user");

  int qret = mysql_query(conn, sql);
  if (qret != 0) {
    fprintf(stderr, "mysql_query: %s\n", mysql_error(conn));
    return -1;
  }

  MYSQL_RES *table = mysql_store_result(conn);
  int n = mysql_num_rows(table);

  if (!n) {
    fprintf(stderr, "init user_table error.\n");
    return -1;
  }
  for (int i = 0; i < n; i++) {
    MYSQL_ROW row = mysql_fetch_row(table);
    int user_id;
    char *user_name = calloc(100, sizeof(char));
    char *salt = calloc(50, sizeof(char));
    char *encrypt = calloc(200, sizeof(char));
    char *prefix = calloc(4096, sizeof(char));
    char *virtual_path = calloc(4096, sizeof(char));
    user_id = atoi(row[0]);
    strcpy(user_name, row[1]);
    strcpy(salt, row[2]);
    strcpy(encrypt, row[3]);
    strcpy(prefix, row[4]);
    strcpy(virtual_path, "/");
    VALTYPE u = {user_id, user_name, salt, encrypt, prefix, virtual_path};
    user_table_put(h, user_name, u);
  }
  return 0;
  // struct passwd* p;
  // setpwent(); // 重置读取位置到开头
  // while ((p = getpwent()) != NULL) {
  //     struct spwd* pwd = getspnam(p->pw_name);
  //     char*        shadow_user = p->pw_name;
  //     // printf("%s\n", shadow_user);
  //     char*        shadow_passwd = pwd->sp_pwdp;
  //     int          idx;
  //     for (idx = strlen(shadow_passwd); idx >= 0; idx--) {
  //         if (shadow_passwd[idx] == '$')
  //             break;
  //     }
  //     if (idx == -1)
  //         continue;
  //     char* user_name = calloc(1024, sizeof(char));
  //     char* salt = calloc(idx + 2 + 1024, sizeof(char));
  //     char* password = calloc(strlen(shadow_passwd) + 1, sizeof(char));
  //     char* prefix = calloc(2048, sizeof(char));
  //     char* virtual_path = calloc(2048, sizeof(char));
  //     strcpy(user_name, shadow_user);
  //     strncpy(salt, shadow_passwd, idx + 1);
  //     strncpy(password, shadow_passwd, strlen(shadow_passwd) + 1);
  //     strcat(prefix, "/tmp/");
  //     strcat(prefix, user_name);
  //     strcat(virtual_path, "/");
  //     VALTYPE tmp = {0, salt, password, prefix, virtual_path};
  //     user_table_put(h, user_name, tmp);
  //     if (access(prefix, 0) != F_OK) {
  //         char init_path[4096] = {0};
  //         strcpy(init_path, prefix);
  //         mkdir(init_path, 0777);
  //     }
  //     //   memset(&tmp, 0, sizeof(VALTYPE));
  //     //   user_table_find(h, shadow_user, &tmp);
  //     // printf("%s %s %s %s %s %d\n", user_name, tmp.salt,
  //     // tmp.encrypt_password,tmp.prefix, tmp.virtual_path, tmp.is_login);
  // }
  // endpwent(); // 关闭用户数据库
  //return 0;
}

static int user_table_grow(userTable *h) {
  int old_capacity = h->capacity;
  int new_capacity =
      old_capacity < 1024 ? 2 * old_capacity : 1.5 * old_capacity;
  hashNode **new_buckets =
      (hashNode **)calloc(new_capacity, sizeof(hashNode *));
  if (!new_buckets) {
    fprintf(stderr, "realloc buckets failed in userTable.c:hash_table_grow\n");
    return -1;
  }
  uint32_t new_seed = time(NULL);
  for (int i = 0; i < old_capacity; i++) {
    hashNode *cur = h->buckets[i];
    while (cur) {
      uint32_t hash_val =
          hash(cur->user_name, strlen(cur->user_name), new_seed);
      int idx = hash_val % new_capacity;
      hashNode *tmp = cur->next;
      cur->next = new_buckets[idx];
      new_buckets[idx] = cur;
      cur = tmp;
    }
  }
  free(h->buckets);
  h->buckets = new_buckets;
  h->capacity = new_capacity;
  h->seed = new_seed;
  return 0;
}

int user_table_put(userTable *h, KEYTYPE key, VALTYPE val) {
  if (h->size * 1.0 / h->capacity > 0.7) {
    if (user_table_grow(h) != 0) {
      fprintf(stderr, "userTable grow failed.\n");
      return -1;
    }
  }
  uint32_t hash_val = hash(key, strlen(key), h->seed);
  int index = hash_val % h->capacity;
  hashNode *cur = h->buckets[index];
  while (cur) {
    if (strcmp(cur->user_name, key) == 0) {
      cur->user_info = val;
      return 0;
    }
    cur = cur->next;
  }
  hashNode *new_node = (hashNode *)calloc(1, sizeof(hashNode));
  new_node->user_name = key;
  new_node->user_info = val;
  new_node->next = h->buckets[index];
  h->buckets[index] = new_node;
  h->size++;
  return 0;
}

int user_table_del(userTable *h, KEYTYPE key) {
  uint32_t hash_val = hash(key, strlen(key), h->seed);
  int index = hash_val % h->capacity;
  hashNode *cur = h->buckets[index];
  hashNode *prev = NULL;
  while (cur) {
    if (strcmp(cur->user_name, key) == 0) {
      if (prev == NULL) {
        h->buckets[index] = cur->next;
      } else {
        prev->next = prev->next->next;
      }
      free(cur);
      h->size--;
      return 0;
    }
    prev = cur;
    cur = cur->next;
  }
  return -1;
}

int user_table_find(userTable *h, KEYTYPE key, VALTYPE *val) {

  uint32_t hash_val = hash(key, strlen(key), h->seed);
  int index = hash_val % h->capacity;
  hashNode *cur = h->buckets[index];
  while (cur) {
    if (strcmp(cur->user_name, key) == 0) {
      *val = cur->user_info;
      return 0;
    }
    cur = cur->next;
  }
  return -1;
}
